/**
 *    Copyright (C) 2008  Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */


#ifndef __MAP_VECTOR_TEST_HPP__
#define __MAP_VECTOR_TEST_HPP__

#include <unistd.h>
#include "TestBase.hpp"

using namespace std;

namespace cubefs    {
namespace core      {
namespace tests     {
namespace map_ns    {

class VectorMapTest : public TestBase<VectorMap<2, BigInt, Wrapper<BigInt>, SimplePageManager> >
{
    CPPUNIT_TEST_SUITE(VectorMapTest);

    //CPPUNIT_TEST(testCreateSmallNew);
    //CPPUNIT_TEST(testCreateSmall100);
    CPPUNIT_TEST(testInsert1000);
    //CPPUNIT_TEST(testInsertBatch);

    //CPPUNIT_TEST(testRemove1000);
    //CPPUNIT_TEST(testRemoveAll);
   // CPPUNIT_TEST(testRemoveBatch);

    CPPUNIT_TEST_SUITE_END();

protected:


public:

    VectorMapTest() {}

    void testCreateSmallNew() {
        map_t m;
        m.mgr() = &mgr;

        Iterator i(&mgr, &txn);
        m.insert(i, 100, 100,txn);

        NodeBase *root = m.get_root(txn);

        CPPUNIT_ASSERT(root != NULL);

        check_tree(root, 100, 1);
        remove_tree(root);
    }

    void testCreateSmall100() {
        map_t m;
        m.mgr() = &mgr;

        NodeBase *root;

        BigInt max = -1;
        for (Int c = 0; c < 100; c++) {
            BigInt key = random(10000);
            
            if (key > max) {
                max = key;
            }

            m.insert(key, key, txn);

            root = m.get_root(txn);
            check_tree(root, max, c + 1);
        }
        root = m.get_root(txn);
        remove_tree(root);
    }

    void testInsert1000() {
        map_t m;
        m.mgr() = &mgr;

        m.max_node_capacity() = 3;

        NodeBase *root;

        BigInt max = -1;
        for (Int c = 0; c < 1000; c++) {
            BigInt key = random(100000);
            cout<<"c="<<c<<" "<<key<<endl;
            
            if (key > max) {
                max = key;
            }

            Iterator i0 = m.insert(key, key, txn);

            root = m.get_root(txn);
            check_tree(root, max, c + 1);

            Iterator i1 = m.find_lt(key - 1, false, txn);

            CPPUNIT_ASSERT_EQUAL(i0.page(), i1.page());
            CPPUNIT_ASSERT_EQUAL(i0.key_idx(), i1.key_idx());
        }
        root = m.get_root(txn);
        remove_tree(root);
    }

    void testInsertBatch() {
        map_t m;
        m.mgr() = &mgr;

        m.max_node_capacity() = 3;

        Int SIZE = 1000;
        makeTree(m, SIZE);

        Iterator i = makeIterator(m, SIZE/2);
        Iterator i1 = i;
        
        CPPUNIT_ASSERT(i1.next_key());

        BigInt key0 = get_key(i.page(), 0, i.key_idx());
        BigInt key1 = get_key(i1.page(), 0, i1.key_idx());

        CPPUNIT_ASSERT(key1 > key0 + 1);

        if (key1 - key0 > 1000) {
            key1 = key0 + 1000;
        }

        for (BigInt c = key0 + 1; c < key1; c++) {
            CPPUNIT_ASSERT(i.next_key());
            m.insert(i, c, c, txn);

            NodeBase *root = m.get_root(txn);
            check_tree(root, get_max(m), SIZE + c - key0);
        }
        NodeBase *root = m.get_root(txn);
        remove_tree(root);
    }

    void testRemove1000() {
        map_t m;

        m.mgr() = &mgr;
        m.max_node_capacity() = 3;

        const Int SIZE = 1000;

        makeTree(m, SIZE);

        for (Int size = SIZE; size > 0; size--) {
            //cout<<"s="<<size<<endl;
            Iterator i = makeIterator(m, random(size));

            if (size == 5) {
                volatile int a;
                a++;
                m.debug() = true;
            }

            m.remove(i, txn);
            
            if (size > 1) {                
                check_tree(m.get_root(txn), get_max(m), size - 1);
            }
        }
        
        //m.dump_tree(txn);
    }

    BigInt get_max(map_t &map) {
        NodeBase *root = map.get_root(txn);
        return map.get_max_key(root, 0);
    }

    void makeTree(map_t &m, Int size) {
        for (Int c = 0; c < size; c++) {
            BigInt key = random(100000);
            m.insert(key, key, txn);
        }
        NodeBase *root = m.get_root(txn);
        check_tree(root, get_max(m), size);
    }

    const Iterator makeIterator(map_t &m, Int skip) {
        NodeBase *root = m.get_root(txn);
        Iterator i = get_first(root);

        for (Int c = 0; c < skip; c++) {
            CPPUNIT_ASSERT(i.next_key());
        }

        return i;
    }

    void testRemoveAll() {
        map_t m;
        
        m.mgr() = &mgr;
        m.max_node_capacity() = 3;

        Int SIZE = 30;
        makeTree(m, SIZE);

        NodeBase *root = m.get_root(txn);

        Iterator i0 = get_first(root);
        Iterator i1 = get_last(root);

        m.remove(i0, i1, txn);

        CPPUNIT_ASSERT(m.root().is_null());
    }

    void testRemoveBatch() {
        map_t m;
        m.mgr() = &mgr;

        m.max_node_capacity() = 3;

        Int SIZE = 1000;
        for (Int c = 0; c < 10; c++) {
            makeTree(m, SIZE);

            Int idx0 = random(SIZE / 2);
            Int idx1 = SIZE / 2 + random(SIZE / 2);

            Iterator i0 = makeIterator(m, idx0);
            Iterator i1 = makeIterator(m, idx1);

            m.remove(i0, i1, txn);

            NodeBase *root = m.get_root(txn);
            check_tree(root, get_max(m), SIZE - (idx1 - idx0));

            i0 = get_first(root);
            i1 = get_last(root);

            m.remove(i0, i1, txn);

            CPPUNIT_ASSERT(m.root().is_null());
        }
    }
 
};

} //map_ns
} //tests
} //core
} //cubefs

#endif

